#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
PDF文件处理模块

提供对PDF文件的读取、创建、修改、合并、拆分等功能
"""

import os
import io
import tempfile
from pathlib import Path
from typing import Union, List, Dict, Any, Optional, Tuple, Iterator, Set

# 导入异常处理装饰器
from ..exception.exception_handler import exception_handler
from ..exception.exceptions import FileException

# 导入日志模块
from ..logger.logger import Logger

# 创建日志记录器
logger = Logger("pdf_handler").get_logger()


class PDFHandler:
    """
    PDF文件处理类
    
    提供对PDF文件的读取、创建、修改、合并、拆分等功能
    
    注意：此类需要安装PyPDF2和reportlab库
    pip install PyPDF2 reportlab
    
    对于OCR功能，还需要安装pytesseract和pdf2image库
    pip install pytesseract pdf2image
    """
    
    def __init__(self):
        """
        初始化PDF处理器
        """
        try:
            import PyPDF2
            from reportlab.pdfgen import canvas
            self.PyPDF2 = PyPDF2
            self.canvas = canvas
        except ImportError as e:
            logger.error(f"初始化PDF处理器失败: {e}")
            logger.error("请安装必要的依赖: pip install PyPDF2 reportlab")
            raise ImportError("请安装必要的依赖: pip install PyPDF2 reportlab") from e
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def get_pdf_info(self, pdf_path: Union[str, Path]) -> Dict[str, Any]:
        """
        获取PDF文件信息
        
        Args:
            pdf_path: PDF文件路径
            
        Returns:
            包含PDF信息的字典，包括页数、元数据、页面尺寸等
            
        Raises:
            FileException: 文件不存在或读取失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> info = handler.get_pdf_info('document.pdf')
            >>> print(f"页数: {info['page_count']}")
            >>> print(f"作者: {info['metadata'].get('Author', '未知')}")
        """
        pdf_path = Path(pdf_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        try:
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                
                # 获取页数
                page_count = len(reader.pages)
                
                # 获取元数据
                metadata = reader.metadata
                if metadata is None:
                    metadata = {}
                else:
                    # 转换为普通字典
                    metadata = dict(metadata)
                
                # 获取第一页尺寸（如果有页面）
                page_sizes = []
                for i in range(min(page_count, 5)):  # 最多获取前5页的尺寸
                    page = reader.pages[i]
                    if '/MediaBox' in page:
                        # MediaBox通常是[0, 0, width, height]
                        media_box = page['/MediaBox']
                        width = float(media_box[2])
                        height = float(media_box[3])
                        page_sizes.append({'width': width, 'height': height})
                
                # 构建结果字典
                result = {
                    'page_count': page_count,
                    'metadata': metadata,
                    'page_sizes': page_sizes,
                    'file_size': pdf_path.stat().st_size,
                    'encrypted': reader.is_encrypted
                }
                
                logger.info(f"获取PDF信息成功: {pdf_path} (页数: {page_count})")
                return result
        except Exception as e:
            raise FileException(f"获取PDF信息失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def extract_text(self, pdf_path: Union[str, Path], page_numbers: Optional[List[int]] = None) -> Dict[int, str]:
        """
        从PDF文件中提取文本
        
        Args:
            pdf_path: PDF文件路径
            page_numbers: 要提取的页码列表（从0开始），默认为None（提取所有页面）
            
        Returns:
            以页码为键、文本内容为值的字典
            
        Raises:
            FileException: 文件不存在或读取失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 提取所有页面的文本
            >>> text_dict = handler.extract_text('document.pdf')
            >>> # 提取特定页面的文本
            >>> text_dict = handler.extract_text('document.pdf', [0, 2])  # 提取第1页和第3页
        """
        pdf_path = Path(pdf_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        try:
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                
                # 获取页数
                page_count = len(reader.pages)
                
                # 如果未指定页码，提取所有页面
                if page_numbers is None:
                    page_numbers = list(range(page_count))
                
                # 验证页码是否有效
                invalid_pages = [p for p in page_numbers if p < 0 or p >= page_count]
                if invalid_pages:
                    raise FileException(f"无效的页码: {invalid_pages}，PDF共有{page_count}页")
                
                # 提取文本
                result = {}
                for page_num in page_numbers:
                    page = reader.pages[page_num]
                    text = page.extract_text()
                    result[page_num] = text
                
                logger.info(f"提取PDF文本成功: {pdf_path} (提取了{len(result)}页)")
                return result
        except Exception as e:
            raise FileException(f"提取PDF文本失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def extract_images(self, pdf_path: Union[str, Path], output_dir: Union[str, Path], 
                     page_numbers: Optional[List[int]] = None) -> List[str]:
        """
        从PDF文件中提取图像
        
        Args:
            pdf_path: PDF文件路径
            output_dir: 图像输出目录
            page_numbers: 要提取的页码列表（从0开始），默认为None（提取所有页面）
            
        Returns:
            提取的图像文件路径列表
            
        Raises:
            FileException: 文件不存在或提取失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 提取所有页面的图像
            >>> image_paths = handler.extract_images('document.pdf', 'output_images')
            >>> # 提取特定页面的图像
            >>> image_paths = handler.extract_images('document.pdf', 'output_images', [0, 2])
        """
        pdf_path = Path(pdf_path)
        output_dir = Path(output_dir)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_dir.mkdir(parents=True, exist_ok=True)
        
        try:
            # 导入必要的库
            try:
                from PIL import Image
            except ImportError:
                raise ImportError("请安装必要的依赖: pip install Pillow")
            
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                
                # 获取页数
                page_count = len(reader.pages)
                
                # 如果未指定页码，提取所有页面
                if page_numbers is None:
                    page_numbers = list(range(page_count))
                
                # 验证页码是否有效
                invalid_pages = [p for p in page_numbers if p < 0 or p >= page_count]
                if invalid_pages:
                    raise FileException(f"无效的页码: {invalid_pages}，PDF共有{page_count}页")
                
                # 提取图像
                image_paths = []
                image_count = 0
                
                for page_num in page_numbers:
                    page = reader.pages[page_num]
                    
                    # 获取页面中的资源
                    if '/Resources' in page and '/XObject' in page['/Resources']:
                        xobjects = page['/Resources']['/XObject']
                        
                        # 遍历所有XObject
                        for obj_name, obj in xobjects.items():
                            # 检查是否为图像
                            if obj['/Subtype'] == '/Image':
                                # 获取图像数据
                                if '/Filter' in obj:
                                    filters = obj['/Filter']
                                    
                                    # 处理不同的图像格式
                                    if isinstance(filters, list):
                                        filter_type = filters[0]
                                    else:
                                        filter_type = filters
                                    
                                    # 根据滤镜类型确定图像格式
                                    if filter_type == '/DCTDecode':
                                        ext = 'jpg'
                                    elif filter_type == '/JPXDecode':
                                        ext = 'jp2'
                                    elif filter_type == '/FlateDecode':
                                        ext = 'png'
                                    else:
                                        ext = 'png'  # 默认使用PNG
                                else:
                                    ext = 'png'  # 默认使用PNG
                                
                                # 构建输出文件路径
                                image_filename = f"page_{page_num+1}_image_{image_count+1}.{ext}"
                                image_path = output_dir / image_filename
                                
                                # 提取图像数据并保存
                                try:
                                    if '/Stream' in obj:
                                        image_data = obj['/Stream'].get_data()
                                        with open(image_path, 'wb') as img_file:
                                            img_file.write(image_data)
                                        image_paths.append(str(image_path))
                                        image_count += 1
                                except Exception as e:
                                    logger.warning(f"提取图像失败: {e}")
                
                logger.info(f"提取PDF图像成功: {pdf_path} -> {output_dir} (提取了{image_count}张图像)")
                return image_paths
        except Exception as e:
            raise FileException(f"提取PDF图像失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def merge_pdfs(self, pdf_paths: List[Union[str, Path]], output_path: Union[str, Path]) -> None:
        """
        合并多个PDF文件
        
        Args:
            pdf_paths: PDF文件路径列表
            output_path: 输出的合并PDF文件路径
            
        Raises:
            FileException: 文件不存在或合并失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> handler.merge_pdfs(['doc1.pdf', 'doc2.pdf', 'doc3.pdf'], 'merged.pdf')
        """
        # 转换为Path对象
        pdf_paths = [Path(p) for p in pdf_paths]
        output_path = Path(output_path)
        
        # 检查所有PDF文件是否存在
        for path in pdf_paths:
            if not path.exists():
                raise FileException(f"PDF文件不存在: {path}")
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            # 创建PDF合并器
            merger = self.PyPDF2.PdfMerger()
            
            # 添加所有PDF文件
            for pdf_path in pdf_paths:
                merger.append(str(pdf_path))
            
            # 写入合并后的PDF
            with open(output_path, 'wb') as output_file:
                merger.write(output_file)
            
            # 关闭合并器
            merger.close()
            
            logger.info(f"合并PDF文件成功: {len(pdf_paths)}个文件 -> {output_path}")
        except Exception as e:
            raise FileException(f"合并PDF文件失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def split_pdf(self, pdf_path: Union[str, Path], output_dir: Union[str, Path], 
                pages_per_file: int = 1) -> List[str]:
        """
        拆分PDF文件
        
        Args:
            pdf_path: PDF文件路径
            output_dir: 输出目录
            pages_per_file: 每个输出文件包含的页数，默认为1（每页一个文件）
            
        Returns:
            拆分后的PDF文件路径列表
            
        Raises:
            FileException: 文件不存在或拆分失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 每页一个文件
            >>> split_files = handler.split_pdf('document.pdf', 'output_dir')
            >>> # 每3页一个文件
            >>> split_files = handler.split_pdf('document.pdf', 'output_dir', pages_per_file=3)
        """
        pdf_path = Path(pdf_path)
        output_dir = Path(output_dir)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_dir.mkdir(parents=True, exist_ok=True)
        
        try:
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                
                # 获取页数
                page_count = len(reader.pages)
                
                # 计算需要拆分的文件数
                file_count = (page_count + pages_per_file - 1) // pages_per_file
                
                # 拆分PDF
                output_files = []
                for i in range(file_count):
                    # 计算当前文件的起始页和结束页
                    start_page = i * pages_per_file
                    end_page = min((i + 1) * pages_per_file, page_count)
                    
                    # 构建输出文件名
                    if file_count == page_count:  # 每页一个文件
                        output_filename = f"{pdf_path.stem}_page_{start_page+1}.pdf"
                    else:  # 多页一个文件
                        output_filename = f"{pdf_path.stem}_pages_{start_page+1}-{end_page}.pdf"
                    
                    output_file_path = output_dir / output_filename
                    
                    # 创建PDF写入器
                    writer = self.PyPDF2.PdfWriter()
                    
                    # 添加页面
                    for page_num in range(start_page, end_page):
                        writer.add_page(reader.pages[page_num])
                    
                    # 写入拆分后的PDF
                    with open(output_file_path, 'wb') as output_file:
                        writer.write(output_file)
                    
                    output_files.append(str(output_file_path))
                    logger.debug(f"创建拆分PDF: {output_file_path} (页码: {start_page+1}-{end_page})")
                
                logger.info(f"拆分PDF文件成功: {pdf_path} -> {output_dir} (创建了{len(output_files)}个文件)")
                return output_files
        except Exception as e:
            raise FileException(f"拆分PDF文件失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def add_watermark(self, pdf_path: Union[str, Path], output_path: Union[str, Path], 
                    watermark_text: str, font_size: int = 40, 
                    opacity: float = 0.3, angle: int = 45) -> None:
        """
        为PDF添加文本水印
        
        Args:
            pdf_path: PDF文件路径
            output_path: 输出的PDF文件路径
            watermark_text: 水印文本
            font_size: 字体大小，默认为40
            opacity: 不透明度（0-1），默认为0.3
            angle: 旋转角度，默认为45度
            
        Raises:
            FileException: 文件不存在或添加水印失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 添加默认水印
            >>> handler.add_watermark('document.pdf', 'watermarked.pdf', '机密文件')
            >>> # 自定义水印参数
            >>> handler.add_watermark('document.pdf', 'watermarked.pdf', '公司内部文件', 
            ...                     font_size=30, opacity=0.2, angle=30)
        """
        pdf_path = Path(pdf_path)
        output_path = Path(output_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            # 导入必要的库
            try:
                from reportlab.pdfgen import canvas
                from reportlab.lib.units import cm
                from reportlab.lib.colors import Color
                from reportlab.pdfbase import pdfmetrics
                from reportlab.pdfbase.ttfonts import TTFont
            except ImportError:
                raise ImportError("请安装必要的依赖: pip install reportlab")
            
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                writer = self.PyPDF2.PdfWriter()
                
                # 获取第一页的尺寸
                if len(reader.pages) > 0:
                    page = reader.pages[0]
                    if '/MediaBox' in page:
                        media_box = page['/MediaBox']
                        width = float(media_box[2])
                        height = float(media_box[3])
                    else:
                        # 默认A4尺寸
                        width = 595
                        height = 842
                else:
                    # 默认A4尺寸
                    width = 595
                    height = 842
                
                # 创建水印
                watermark_buffer = io.BytesIO()
                c = canvas.Canvas(watermark_buffer, pagesize=(width, height))
                
                # 设置字体和颜色
                c.setFont("Helvetica", font_size)
                c.setFillColor(Color(0, 0, 0, alpha=opacity))  # 黑色，带透明度
                
                # 计算水印位置和间距
                x_step = font_size * 3
                y_step = font_size * 3
                
                # 在整个页面上重复绘制水印
                c.saveState()
                c.translate(width/2, height/2)  # 移动到页面中心
                c.rotate(angle)  # 旋转
                
                # 绘制水印网格
                for x in range(int(-width), int(width*2), x_step):
                    for y in range(int(-height), int(height*2), y_step):
                        c.drawString(x, y, watermark_text)
                
                c.restoreState()
                c.save()
                
                # 创建水印页面
                watermark_buffer.seek(0)
                watermark_pdf = self.PyPDF2.PdfReader(watermark_buffer)
                watermark_page = watermark_pdf.pages[0]
                
                # 将水印应用到每一页
                for i in range(len(reader.pages)):
                    page = reader.pages[i]
                    page.merge_page(watermark_page)
                    writer.add_page(page)
                
                # 写入带水印的PDF
                with open(output_path, 'wb') as output_file:
                    writer.write(output_file)
                
                logger.info(f"添加PDF水印成功: {pdf_path} -> {output_path}")
        except Exception as e:
            raise FileException(f"添加PDF水印失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def add_page_numbers(self, pdf_path: Union[str, Path], output_path: Union[str, Path], 
                       start_number: int = 1, position: str = 'bottom-center',
                       font_size: int = 10) -> None:
        """
        为PDF添加页码
        
        Args:
            pdf_path: PDF文件路径
            output_path: 输出的PDF文件路径
            start_number: 起始页码，默认为1
            position: 页码位置，可选'bottom-center'、'bottom-right'、'bottom-left'，默认为'bottom-center'
            font_size: 字体大小，默认为10
            
        Raises:
            FileException: 文件不存在或添加页码失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 添加默认页码
            >>> handler.add_page_numbers('document.pdf', 'numbered.pdf')
            >>> # 自定义页码参数
            >>> handler.add_page_numbers('document.pdf', 'numbered.pdf', 
            ...                        start_number=5, position='bottom-right', font_size=12)
        """
        pdf_path = Path(pdf_path)
        output_path = Path(output_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            # 导入必要的库
            try:
                from reportlab.pdfgen import canvas
                from reportlab.lib.units import cm
            except ImportError:
                raise ImportError("请安装必要的依赖: pip install reportlab")
            
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                writer = self.PyPDF2.PdfWriter()
                
                # 处理每一页
                for i in range(len(reader.pages)):
                    page = reader.pages[i]
                    page_num = i + start_number
                    
                    # 获取页面尺寸
                    if '/MediaBox' in page:
                        media_box = page['/MediaBox']
                        width = float(media_box[2])
                        height = float(media_box[3])
                    else:
                        # 默认A4尺寸
                        width = 595
                        height = 842
                    
                    # 创建页码
                    packet = io.BytesIO()
                    c = canvas.Canvas(packet, pagesize=(width, height))
                    c.setFont("Helvetica", font_size)
                    
                    # 设置页码位置
                    if position == 'bottom-center':
                        x = width / 2
                        y = 20
                    elif position == 'bottom-right':
                        x = width - 50
                        y = 20
                    elif position == 'bottom-left':
                        x = 50
                        y = 20
                    else:  # 默认底部中心
                        x = width / 2
                        y = 20
                    
                    # 绘制页码
                    c.drawString(x, y, str(page_num))
                    c.save()
                    
                    # 创建页码页面
                    packet.seek(0)
                    number_pdf = self.PyPDF2.PdfReader(packet)
                    number_page = number_pdf.pages[0]
                    
                    # 合并页码到原页面
                    page.merge_page(number_page)
                    writer.add_page(page)
                
                # 写入带页码的PDF
                with open(output_path, 'wb') as output_file:
                    writer.write(output_file)
                
                logger.info(f"添加PDF页码成功: {pdf_path} -> {output_path}")
        except Exception as e:
            raise FileException(f"添加PDF页码失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def rotate_pages(self, pdf_path: Union[str, Path], output_path: Union[str, Path], 
                   rotation: int, page_numbers: Optional[List[int]] = None) -> None:
        """
        旋转PDF页面
        
        Args:
            pdf_path: PDF文件路径
            output_path: 输出的PDF文件路径
            rotation: 旋转角度，可选90、180、270
            page_numbers: 要旋转的页码列表（从0开始），默认为None（旋转所有页面）
            
        Raises:
            FileException: 文件不存在或旋转失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 旋转所有页面90度
            >>> handler.rotate_pages('document.pdf', 'rotated.pdf', 90)
            >>> # 只旋转特定页面
            >>> handler.rotate_pages('document.pdf', 'rotated.pdf', 180, [0, 2])  # 旋转第1页和第3页
        """
        pdf_path = Path(pdf_path)
        output_path = Path(output_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        # 验证旋转角度
        if rotation not in [90, 180, 270]:
            raise FileException(f"无效的旋转角度: {rotation}，只支持90、180、270度")
        
        try:
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                writer = self.PyPDF2.PdfWriter()
                
                # 获取页数
                page_count = len(reader.pages)
                
                # 如果未指定页码，旋转所有页面
                if page_numbers is None:
                    page_numbers = list(range(page_count))
                
                # 验证页码是否有效
                invalid_pages = [p for p in page_numbers if p < 0 or p >= page_count]
                if invalid_pages:
                    raise FileException(f"无效的页码: {invalid_pages}，PDF共有{page_count}页")
                
                # 处理每一页
                for i in range(page_count):
                    page = reader.pages[i]
                    
                    # 如果当前页需要旋转
                    if i in page_numbers:
                        page.rotate(rotation)
                    
                    writer.add_page(page)
                
                # 写入旋转后的PDF
                with open(output_path, 'wb') as output_file:
                    writer.write(output_file)
                
                logger.info(f"旋转PDF页面成功: {pdf_path} -> {output_path} (旋转了{len(page_numbers)}页)")
        except Exception as e:
            raise FileException(f"旋转PDF页面失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def encrypt_pdf(self, pdf_path: Union[str, Path], output_path: Union[str, Path], 
                 user_password: str, owner_password: Optional[str] = None) -> None:
        """
        加密PDF文件
        
        Args:
            pdf_path: PDF文件路径
            output_path: 输出的加密PDF文件路径
            user_password: 用户密码（打开文档所需的密码）
            owner_password: 所有者密码（编辑文档所需的密码），默认为None（与用户密码相同）
            
        Raises:
            FileException: 文件不存在或加密失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 使用相同的用户密码和所有者密码
            >>> handler.encrypt_pdf('document.pdf', 'encrypted.pdf', 'password123')
            >>> # 使用不同的用户密码和所有者密码
            >>> handler.encrypt_pdf('document.pdf', 'encrypted.pdf', 'user_pass', 'owner_pass')
        """
        pdf_path = Path(pdf_path)
        output_path = Path(output_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        # 如果未指定所有者密码，使用用户密码
        if owner_password is None:
            owner_password = user_password
        
        try:
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                writer = self.PyPDF2.PdfWriter()
                
                # 复制所有页面
                for page in reader.pages:
                    writer.add_page(page)
                
                # 加密PDF
                writer.encrypt(user_password=user_password, owner_password=owner_password)
                
                # 写入加密后的PDF
                with open(output_path, 'wb') as output_file:
                    writer.write(output_file)
                
                logger.info(f"加密PDF文件成功: {pdf_path} -> {output_path}")
        except Exception as e:
            raise FileException(f"加密PDF文件失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def decrypt_pdf(self, pdf_path: Union[str, Path], output_path: Union[str, Path], 
                  password: str) -> None:
        """
        解密PDF文件
        
        Args:
            pdf_path: PDF文件路径
            output_path: 输出的解密PDF文件路径
            password: 密码
            
        Raises:
            FileException: 文件不存在、密码错误或解密失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> handler.decrypt_pdf('encrypted.pdf', 'decrypted.pdf', 'password123')
        """
        pdf_path = Path(pdf_path)
        output_path = Path(output_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            # 打开PDF文件
            with open(pdf_path, 'rb') as file:
                reader = self.PyPDF2.PdfReader(file)
                
                # 检查PDF是否加密
                if not reader.is_encrypted:
                    raise FileException(f"PDF文件未加密: {pdf_path}")
                
                # 尝试解密
                try:
                    reader.decrypt(password)
                except Exception:
                    raise FileException(f"密码错误或PDF无法解密: {pdf_path}")
                
                # 创建新的PDF
                writer = self.PyPDF2.PdfWriter()
                
                # 复制所有页面
                for page in reader.pages:
                    writer.add_page(page)
                
                # 写入解密后的PDF
                with open(output_path, 'wb') as output_file:
                    writer.write(output_file)
                
                logger.info(f"解密PDF文件成功: {pdf_path} -> {output_path}")
        except FileException:
            raise
        except Exception as e:
            raise FileException(f"解密PDF文件失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def create_pdf_from_text(self, text: str, output_path: Union[str, Path], 
                          title: Optional[str] = None, author: Optional[str] = None,
                          font_size: int = 12, page_size: Tuple[int, int] = (595, 842)) -> None:
        """
        从文本创建PDF文件
        
        Args:
            text: 文本内容
            output_path: 输出的PDF文件路径
            title: 文档标题，默认为None
            author: 文档作者，默认为None
            font_size: 字体大小，默认为12
            page_size: 页面尺寸（宽度，高度），默认为A4尺寸(595, 842)
            
        Raises:
            FileException: 创建失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 创建简单的PDF
            >>> handler.create_pdf_from_text('Hello, World!', 'hello.pdf')
            >>> # 创建带有元数据的PDF
            >>> handler.create_pdf_from_text('PDF内容', 'document.pdf', 
            ...                           title='测试文档', author='测试作者', font_size=14)
        """
        output_path = Path(output_path)
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            # 导入必要的库
            try:
                from reportlab.pdfgen import canvas
                from reportlab.lib.pagesizes import A4
                from reportlab.lib.styles import getSampleStyleSheet
                from reportlab.platypus import SimpleDocTemplate, Paragraph
                from reportlab.lib.units import inch
            except ImportError:
                raise ImportError("请安装必要的依赖: pip install reportlab")
            
            # 创建PDF文档
            doc = SimpleDocTemplate(
                str(output_path),
                pagesize=page_size,
                title=title,
                author=author
            )
            
            # 设置样式
            styles = getSampleStyleSheet()
            style = styles['Normal']
            style.fontSize = font_size
            
            # 将文本分割为段落
            paragraphs = []
            for line in text.split('\n'):
                if line.strip():
                    paragraphs.append(Paragraph(line, style))
                else:
                    # 添加空行
                    paragraphs.append(Paragraph('<br/>', style))
            
            # 构建文档
            doc.build(paragraphs)
            
            logger.info(f"从文本创建PDF成功: {output_path}")
        except Exception as e:
            raise FileException(f"从文本创建PDF失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def pdf_to_images(self, pdf_path: Union[str, Path], output_dir: Union[str, Path], 
                    dpi: int = 200, image_format: str = 'png') -> List[str]:
        """
        将PDF转换为图像
        
        Args:
            pdf_path: PDF文件路径
            output_dir: 输出目录
            dpi: 图像分辨率，默认为200
            image_format: 图像格式，可选'png'、'jpg'、'tiff'等，默认为'png'
            
        Returns:
            生成的图像文件路径列表
            
        Raises:
            FileException: 文件不存在或转换失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 默认设置
            >>> image_paths = handler.pdf_to_images('document.pdf', 'output_images')
            >>> # 自定义设置
            >>> image_paths = handler.pdf_to_images('document.pdf', 'output_images', dpi=300, image_format='jpg')
        """
        pdf_path = Path(pdf_path)
        output_dir = Path(output_dir)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_dir.mkdir(parents=True, exist_ok=True)
        
        try:
            # 导入必要的库
            try:
                from pdf2image import convert_from_path
            except ImportError:
                raise ImportError("请安装必要的依赖: pip install pdf2image poppler-utils")
            
            # 转换PDF为图像
            images = convert_from_path(pdf_path, dpi=dpi)
            
            # 保存图像
            image_paths = []
            for i, image in enumerate(images):
                image_filename = f"{pdf_path.stem}_page_{i+1}.{image_format}"
                image_path = output_dir / image_filename
                image.save(str(image_path), format=image_format.upper())
                image_paths.append(str(image_path))
            
            logger.info(f"PDF转换为图像成功: {pdf_path} -> {output_dir} (共 {len(image_paths)} 张图像)")
            return image_paths
        except Exception as e:
            raise FileException(f"PDF转换为图像失败: {e}")
    
    @exception_handler(exception_type=Exception, reraise_as=FileException)
    def pdf_ocr(self, pdf_path: Union[str, Path], output_path: Union[str, Path], 
              language: str = 'chi_sim+eng', dpi: int = 300) -> None:
        """
        对PDF文件进行OCR识别，生成可搜索的PDF
        
        Args:
            pdf_path: PDF文件路径
            output_path: 输出的PDF文件路径
            language: OCR语言，默认为'chi_sim+eng'（简体中文+英文）
            dpi: 图像分辨率，默认为300
            
        Raises:
            FileException: 文件不存在或OCR失败时抛出
            
        Examples:
            >>> handler = PDFHandler()
            >>> # 默认设置（中文+英文）
            >>> handler.pdf_ocr('scan.pdf', 'searchable.pdf')
            >>> # 仅英文
            >>> handler.pdf_ocr('scan.pdf', 'searchable.pdf', language='eng')
        """
        pdf_path = Path(pdf_path)
        output_path = Path(output_path)
        
        # 检查PDF文件是否存在
        if not pdf_path.exists():
            raise FileException(f"PDF文件不存在: {pdf_path}")
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            # 导入必要的库
            try:
                import pytesseract
                from pdf2image import convert_from_path
                from PIL import Image
            except ImportError:
                raise ImportError("请安装必要的依赖: pip install pytesseract pdf2image Pillow")
            
            # 创建临时目录
            with tempfile.TemporaryDirectory() as temp_dir:
                temp_dir_path = Path(temp_dir)
                
                # 将PDF转换为图像
                images = convert_from_path(pdf_path, dpi=dpi)
                
                # 创建PDF合并器
                merger = self.PyPDF2.PdfMerger()
                
                # 处理每一页
                for i, image in enumerate(images):
                    # 保存图像
                    image_path = temp_dir_path / f"page_{i+1}.png"
                    image.save(str(image_path))
                    
                    # OCR识别
                    pdf_path_ocr = temp_dir_path / f"page_{i+1}_ocr.pdf"
                    pytesseract.image_to_pdf_or_hocr(
                        str(image_path),
                        extension='pdf',
                        lang=language,
                        output_file=str(pdf_path_ocr)
                    )
                    
                    # 添加到合并器
                    merger.append(str(pdf_path_ocr))
                
                # 写入合并后的PDF
                with open(output_path, 'wb') as output_file:
                    merger.write(output_file)
                
                # 关闭合并器
                merger.close()
            
            logger.info(f"PDF OCR识别成功: {pdf_path} -> {output_path}")
        except Exception as e:
            raise FileException(f"PDF OCR识别失败: {e}")